import warnings
from enum import Enum, unique
from pathlib import Path
from typing import Any, Dict, List, Literal, Optional, Sequence, TypeAlias, Union
import numpy
import pandas
from .geoProfileGpro import PreProfile1D
from .getUniformProfileFuncGpro import find_node_by_id
from .getUniformProfileFuncOptimizedGpro import get_uniform_profile1DSet_optimized
from ..dataclass import GDIDataQualityWarning
from .geoProfileGpro import PreProfile1D
from .getUniformProfileFuncGpro import find_node_by_id, get_uniform_profile1DSet
from .getUniformProfileFuncOptimizedGpro import get_uniform_profile1DSet_optimized

class VerticalBore:
    """Object for the single vertical bore."""
    def __init__(self, top: Optional[float] = None, num: Optional[str] = None, x_coord: Optional[float] = None, y_coord: Optional[float] = None, layers_depth: Optional[Any] = None, materials_id: Optional[Any] = None, layers_num: Optional[Any] = None, materials_name: Optional[Any] = None, samples_depth: Any = None, samples_id: Any = None) -> Any:
        """
        "Initialize the VerticalBore object.
        
        Parameters
        ----------
        top : float, default: None
            The top elevation of the bore.
        
        num : str, default: None
            The number of the bore.
        
        layers_depth : Sequence[float], default: None
            The bottom depth of each layer.
        
        layers_num : Sequence[str], default: None
            The layer's number of each layer.  if it has sub-layer or sub-sub-layer,the string must be separated by "-", like 1-1 or 1-1-1
        
        materials_id : Sequence[int], default: None
            The material id for each layer, it should have the same length with layers_depth
        
        materials_name : Dict[int, str], default: None
            The materials mapping between materials id and materials name. The key is id and the value is the name.
        
        Notes
        -----
        - For a vertical bore with corret data, the length of the layers_depth and materials_id should be same. And each material id should have a name mapping in materials_name.
        - If top is None, it means it's an empty bore.
        """
        ...
    @property
    def layers_thickness(self) -> List[float]:
        """Return the thickness of each layer."""
        ...
    @property
    def layers_elevation(self) -> List[float]:
        """Return the bottom elevation of each layer."""
        ...
    @layers_elevation.setter
    def layers_elevation(self, elevations: Sequence[float], top_included: bool = False) -> Any:
        """Define the layer location by elevation."""
        ...
    @property
    def samples_elevation(self) -> Any:
        """Return the elevation of each sample."""
        ...
    @samples_elevation.setter
    def samples_elevation(self, elevations: Sequence[float]) -> Any:
        """Define the sample location by elevation."""
        ...
    def data_check(self) -> Any:
        """Check whether the data of the bore is valid."""
        ...

class VerticalBores:
    """Object for a list of vertical bores."""
    def __init__(self, vertical_bores: Optional[Any] = None, materials_name: Optional[Any] = None, bores_table: Optional[Any] = None, layers_table: Optional[Any] = None, samples_table: Optional[Any] = None) -> Any:
        """
        Initialize VerticalBores object.
        
        Parameters
        ----------
        vertical_bores: List[VerticalBore], default: None
            If it's not None, bores_table and layers_table will be ignored.
        
        materials_name: Dict[int, str], default: None
            The materials mapping between materials id and materials name. The key is id and the value is the name.
        
        bores_table: pd.DataFrame, default: None
            Data of bores information which has columns `["num", "top", "x_coord", "y_coord"]`.
        
        layers_table: pd.DataFrame, default: None
            Data of layers information which has columns `["num", "layers_depth", "materials_id", "layers_num",  "materials"]`.
        """
        ...
    @property
    def bbox(self) -> tuple[float, float, float, float]:
        """(x_min, x_max, y_min, y_max)"""
        ...
    def get_bore(self, num: str) -> Optional[VerticalBore]:
        """Get a single VerticalBore by bore number."""
        ...
    def get_bores(self, row_indices: Any) -> Any:
        """
        Get VerticalBores by row indices of bores_table.
        
        Parameters
        ----------
        row_indices: list[int] or np.ndarray[int]
            The row indices of bores_table.
        """
        ...
    def get_samples_position(self) -> pd.DataFrame:
        """Get the position of all samples including the xy coordinates."""
        ...
    def add_bore(self, bore: VerticalBore) -> Any:
        """Add a bore in the data table."""
        ...
    def remove_bore(self, num: str) -> Any:
        """Remove a bore from the data table."""
        ...
    def data_check(self) -> Any:
        """Check whether the data of the tables are valid."""
        ...

class UniVerticalBore(VerticalBore):
    """Object for the single uni_vertical bore."""
    def __init__(self, top: Optional[float] = None, num: Optional[str] = None, x_coord: Optional[float] = None, y_coord: Optional[float] = None, layers_depth: Optional[Any] = None, layers_num: Optional[Any] = None, materials_id: Optional[Any] = None, materials_name: Optional[Any] = None, hierarchys_depth: Optional[Any] = None, hierarchys_material_id: Optional[Any] = None, hierarchys_id: Optional[Any] = None) -> Any:
        """
        "Initialize the UniVerticalBore object.
        
        Parameters
        ----------
        top : float, default: None
            The top elevation of the bore.
        
        num : str, default: None
            The number of the bore.
        
        layers_depth : Sequence[float], default: None
            The bottom depth of each layer.
        
        layers_num : Sequence[str], default: None
            The layer's number of each layer.  if it has sub-layer or sub-sub-layer,the string must be separated by "-", like 1-1 or 1-1-1
        
        materials_id : Sequence[int], default: None
            The material id for each layer, it should have the same length with layers_depth
        
        materials_name : Dict[int, str], default: None
            The materials mapping between materials id and materials name. The key is id and the value is the name.
        
        
        
        hierarchys_depth : Sequence[float], default: None
            The bottom depth of each hierarchy.
        
        hierarchys_id : Sequence[int], default: None
            TThe  material id for each hierarchy, it should have the same length with hierarchys_depth
        
        Notes
        -----
        - For a uni_vertical bore with corret data, the length of the hierarchys_depth and hierarchys_id should be same. And each hierarchy id should have a name mapping in materials_name.
        - If top is None, it means it's an empty bore.
        """
        ...

class UniVerticalBores(VerticalBores):
    """Vertical bores after uniforming"""
    def __init__(self, vertical_bores: Optional[VerticalBores] = None) -> Any:
        """
        Initialize UniVerticalBores object.
        
        Parameters
        ----------
        vertical_bores: VerticalBores, default: None
        """
        ...
    def run(self, use_layer_num: bool = True):
        """
        uniforming verticalBore in the tables,and generate the hierarchys_table's value
        :param use_layer_num: using layerNum in uniforming process
        :return:
        """
        ...
    def add_unibore(self, bore: UniVerticalBore) -> Any:
        """Add a bore in the data table."""
        ...
    def get_bores(self, row_indices: Any) -> Any:
        """
        Get UniVerticalBores by row indices of bores_table.
        
        Parameters
        ----------
        row_indices: list[int] or np.ndarray[int]
            The row indices of bores_table.
        """
        ...
    def data_check(self) -> Any:
        """Check whether the data of the tables are valid."""
        ...
    def from_uni_preProfile1dset(self, boring_set: List[PreProfile1D], materials_name: Optional[Any]) -> List[UniVerticalBore]:
        """
        convert uniformed PreProfile1d objects to UniVerticalBore objects
        
        :param boring_set: PreProfile1d objects conterted from VerticalBores，which have been uniformed. Source_VerticalBores' materials_name must be the same as the  return instance.
        :return: UniVerticalBore object list
        """
        ...
    @property
    def nlay(self) -> Any:
        ...
    @property
    def layers_id(self) -> list[int]:
        ...
    @property
    def hierarchy_materials_id(self) -> list[int]:
        ...
    @property
    def layers_name(self) -> dict[int, str]:
        ...
    def to_multisurface_points(self, include_ground_points: bool = True, pinch_factor: float = 0) -> tuple[Any, Any]:
        """
        Convert UniVerticalBores to MultiSurfacePoints and ground points for parallel interpolation.
        
        This method extracts layer bottom surfaces from the bores and creates separate surface point datasets
        that can be interpolated in parallel, unlike the sequential bore-based approach.
        
        Parameters
        ----------
        include_ground_points : bool, default: True
            If True, creates ground points from bore tops.
        
        pinch_factor : float, default: 0
            Factor used to set the degree of pinch for pinch locations.
            - 0: Pinch bottom = previous layer bottom (zero thickness)
            - >0: Pinch bottom higher than previous layer bottom (negative thickness, upward growth)
        
        Returns
        -------
        tuple[MultiSurfacePoints, SurfacePoints]
            - MultiSurfacePoints: Contains all layer bottom surfaces
            - SurfacePoints: Ground surface points from bore tops (if include_ground_points=True)
        
        Notes
        -----
        - Pinch locations (depth = -1) are handled according to pinch_factor
        - Unknown depths (depth = -2) are excluded from the surface points
        - This conversion enables parallel interpolation while preserving pinch factor functionality
        """
        ...
